﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Library.OrmEnterpriseEntity.Factor;
using Library.OrmEnterpriseEntity.TableInfo;

namespace Library.OrmEnterpriseEntity {
	///<summary>
	/// Base class for PersistentEntity
	///</summary>
	public class GlobalEntity : Table {

		internal virtual Int16Column ColumnRowType { get { return null; } }

		protected GlobalEntityView GlobalEntityMetadata { get; private set; }

		private const short DefaultSfuvc = -1;

		private class SfuvcKey : Int16Column {
			public SfuvcKey(DbColumnView metadata, DbTable table)
				: base(metadata, table) {
			}
			internal override bool ValueNotDefault {
				get { return Value != DefaultSfuvc; }
			}

			protected override short Default {
				get { return DefaultSfuvc; }
			}
		}

		//protected bool IsMetadata { get { return GlobalEntityMetadata == null; } }
		/// <summary>
		/// Unique id for all row type
		/// </summary>
		public Int64Column RowId { get; private set; }
		/// <summary>
		/// SELECT FOR UPDATE VERSION CONTROL
		/// </summary>
		public Int16Column Sfuvc { get; private set; }
		protected static object[] EmpltyValue = new object[0];
		protected static string[] EmpltyParameter = new string[0];
		protected static DbType[] EmplTypes = new DbType[0];
		/// <summary>
		/// Create entity instance
		/// </summary>
		//public GlobalEntity()
		//  : this(null) {
		//}

		protected GlobalEntity(GlobalEntityView metadata)
			: base(metadata) {
			BeginInit();
			GlobalEntityMetadata = metadata;

			RowId = new Int64Column(GlobalEntityMetadata.RowId, this);
			Sfuvc = new SfuvcKey(GlobalEntityMetadata.Sfuvc, this);

			EndInit();
		}



		//protected override void Initialize() {
		//  base.Initialize();

		//}

		private static IDbContext Context {
			get { return DbContext.Instance.Context; }
		}
		/// <summary>
		/// Default command type: CommandType.Text
		/// </summary>
		protected CommandType DefaultCommandType {
			get { return CommandType.Text; }
		}
		//internal virtual DbColumn ColumnRowType { get { return null; } }

		//internal virtual QueryExpBuilder InnerExpression(QueryStatement statement) {
		//  return null;
		//}
		protected static QueryExpBuilder AndOrCreate(QueryExpBuilder queryExpBuilder, DbColumnView column, QueryOperator queryOperator, object value) {
			if (column == null) {
				throw new ArgumentNullException("column");
			}
			if (queryExpBuilder != null)
				return queryExpBuilder.And(column, queryOperator, value);
			return new QueryExpBuilder(column, queryOperator, value);
		}
		protected static QueryExpBuilder OrOrCreate(QueryExpBuilder queryExpBuilder, DbColumnView column, QueryOperator queryOperator, object value) {
			if (column == null) {
				throw new ArgumentNullException("column");
			}
			if (queryExpBuilder != null)
				return queryExpBuilder.Or(column, queryOperator, value);
			return new QueryExpBuilder(column, queryOperator, value);
		}


		/// <summary>
		/// Save entity to database
		/// </summary>
		/// <returns></returns>
		public virtual SaveResult Save() {
			if (!ValidateSave()) return SaveResult.None;
			if (MarkDelete) {
				if (Delete()) return SaveResult.Deleted;
				return SaveResult.None;
			}
			//if (IsMetadata)
			//  return SaveResult.None;

			switch (State) {
				case EntityState.New:
					if (Insert())
						return SaveResult.Inserted;
					return SaveResult.None;
				case EntityState.DbChange:
					if (Update()) return SaveResult.Updated;
					return SaveResult.None;
			}

			return SaveResult.None;
		}
		protected virtual bool DoInsert() {
			return Context.DoInsert(this);

		}

		private bool Insert() {
			if (!ValidateInsert())
				return false;
			Sfuvc.Value = 0;
			return DoInsert();
		}
		protected virtual bool DoUpdate() {
			return Context.DoUpdate(this);

		}
		private bool Update() {
			if (!ValidateUpdate())
				return false;
			//var bs = new byte[2];
			//while (!Sfuvc.ValueDbChange) {
			//  new Random().NextBytes(bs);
			//  Sfuvc.Value = BitConverter.ToInt16(bs, 0);
			//}
			//if (Sfuvc.Value == DefaultSfuvc) Sfuvc.Value = 0;
			do {
				Sfuvc.Value++;
				if (Sfuvc.Value == Int16.MaxValue) 
					Sfuvc.Value = 0;
			} while (!Sfuvc.ValueDbChange);
			return DoUpdate();
		}
		protected virtual bool DoDelete() {
			using (var localTrans = TransactionManager.CreateLocalTrans()) {

				var cascades = GlobalEntityMetadata.DeleteCascade();
				if (cascades != null) {
					foreach (var relationView in cascades) {
						bool breakDelete = false;
						QueryExpBuilder where = null;
						foreach (var relationDetailView in relationView.Details) {
							// i am parent
							var currentColumn = Columns.Find(relationDetailView.RefColumn.ColumnIndex);
							switch (relationDetailView.RelationType) {
								case RelationDetailType.Column2ColumnRef:
									where = AndOrCreate(where, relationDetailView.Column, QueryOperator.Equal,
																			currentColumn.DbValue);
									break;
								case RelationDetailType.ColumnConstant:
									where = AndOrCreate(where, relationDetailView.Column, QueryOperator.Equal, relationDetailView.Value);
									break;
								case RelationDetailType.ConlumnRefConstant:
									if (relationDetailView.Value != currentColumn.DbValue) {
										// do not delete cascade becase delete condition not match
										breakDelete = true;
									}
									break;
							}
						}
						if (!breakDelete) {
							if (where == null) {
								// delete all row in the table, may be error in design. throw exception ,
								throw new DeleteException(String.Format("Cascade detete in relation {0} has no where clause", relationView.Name));
							}
							if (relationView.TableView.HasDeleteCascade || relationView.TableView.HasDeleteRestrict) {
								var creator = new GlobalViewCreator(relationView.TableView);
								QueryRun
									.Select(relationView.TableView.Columns)
									.From(relationView.TableView)
									.Where(where)
									.Excute(creator);
								foreach (var globalEntity in creator.Entities) {
									globalEntity.Delete();
								}
							} else {
								QueryRun
									.Delete(relationView.TableView)
									.Where(where)
									.Excute();
							}
						}
					}

				}

				var result = Context.DoDelete(this);
				localTrans.Commit();
				return result;

			}

			//return Context.DoDelete(this);
		}
		/// <summary>
		/// Delete entity
		/// </summary>
		/// <returns></returns>
		public bool Delete() {
			//if (IsMetadata) return false;
			if (State != EntityState.DbNoChange && State != EntityState.DbChange)
				return false;
			if (!ValidateDelete())
				return false;
			return DoDelete();
		}


		/// <summary>
		/// Dispose
		/// </summary>
		public void Dispose() {
			GC.SuppressFinalize(this);
		}

		//protected virtual GlobalEntity CreateInstance() {
		//  return GlobalEntityMetadata.CreateInstance();
		//  //return new GlobalEntity(GlobalEntityMetadata);
		//}
		/// <summary>
		/// Validate all contrain before do insert object to database
		/// </summary>
		/// <exception cref="ValidattionException"></exception>
		/// <returns>true: allow do insert, otherwise do nothing</returns>
		public virtual bool ValidateInsert() {
			Validate();
			ValidateUnique(true);
			//ValidateExist(true);
			return true;
		}

		/// <summary>
		/// Validate for save action
		/// </summary>
		/// <returns>true: can perform save action, otherwise return false</returns>
		public virtual bool ValidateSave() {
			return true;
		}

		/// <summary>
		/// Valite delete action
		/// </summary>
		/// <returns>true: can peform delete action, otherwise return false</returns>
		public virtual bool ValidateDelete() {
			var restricts = GlobalEntityMetadata.DeleteRestrict();
			if (restricts != null) {

				foreach (var restrict in restricts) {
					bool breakRestrict = false;
					QueryExpBuilder where = null;
					foreach (var relationDetailView in restrict.Details) {
						// i am parent 
						var currentColumn = Columns.Find(relationDetailView.Column.ColumnIndex);
						switch (relationDetailView.RelationType) {
							case RelationDetailType.Column2ColumnRef:
								where = AndOrCreate(where, relationDetailView.RefColumn, QueryOperator.Equal,
																		currentColumn.DbValue);
								break;
							//case RelationDetailType.ColumnAndColumnRefConstant:
							//  if (relationDetailView.ColumnValue != currentColumn.DbValue) {
							//    // do not delete cascade becase delete condition not match
							//    breakRestrict = true;
							//  } else {
							//    where = AndOrCreate(where, relationDetailView.Column, QueryOperator.Equal, relationDetailView.ColumnValue);
							//  }
							//  break;
							case RelationDetailType.ConlumnRefConstant:
								where = AndOrCreate(where, relationDetailView.RefColumn, QueryOperator.Equal, relationDetailView.Value);
								break;
							case RelationDetailType.ColumnConstant:
								if (relationDetailView.Value != currentColumn.DbValue) {
									// do not delete cascade becase delete condition not match
									breakRestrict = true;
								}
								break;
						}
					}

					if (!breakRestrict) {
						var creator = new OneEntityCreator(restrict.TableView.CreateInstance());
						QueryRun
							.Select(restrict.TableView.RowId)
							.From(restrict.TableView)
							.Where(where)
							.Excute(creator, 1);
						if (creator.Success) {
							var columns = new List<DbColumn>();
							foreach (var relationView in restrict.Details) {
								var view = relationView;
								if (view.Column == null) continue;
								var col = Columns.FirstOrDefault(c => c.ColumnView == view.Column);
								if (col != null) columns.Add(col);
							}
							var arrCols = columns.ToArray();

							var e = new ThrowValidationExceptionEventArgs(arrCols, ValidationError.DeleteTransExist, restrict.TableView);
							OnThrowValidation(e);
							var error = e.Message;
							if (String.IsNullOrEmpty(error))
								error = DbContext.Instance.Context.StringResource.DeleteTransExist(arrCols, e.ColumnNames, e.ColumnValues,
																																											e.TableName);
							throw new ValidateDeleteException(error, arrCols);

							//throw new RestrictDeleteException(String.Format("Delete table {0} cause restrict error in table {1}", restrict.ParentTableView.TableName, restrict.TableView.TableName), restrict);
						}
					}
				}
			}


			/*
			var restrict = GlobalEntityMetadata.DeleteRestrict();
			if (restrict == null) return true;
			foreach (var relationViews in restrict) {
				DbColumnView[] columnViews;
				object[] values;
				GlobalEntityView table;
				if (ParseRelation(relationViews.Details, "DeleteRestrict", out columnViews, out values, out table)) {
					QueryExpBuilder where = null;
					for (int i = 0; i < columnViews.Length; i++) {
						where = AndOrCreate(where, columnViews[i], QueryOperator.Equal, values[i]);
					}
					if (where == null) continue;

					var creator = new OneEntityCreator(table.CreateInstance());
					QueryRun
						.Select(table.RowId)
						.From(table)
						.Where(where)
						.Excute(creator, 1);


					if (creator.Success) {
						var columns = new List<DbColumn>();
						foreach (var relationView in relationViews.Details) {
							var view = relationView;
							if (view.Column == null) continue;
							var col = Columns.FirstOrDefault(c => c.ColumnView == view.Column);
							if (col != null) columns.Add(col);
						}
						var arrCols = columns.ToArray();

						var e = new ThrowValidationExceptionEventArgs(arrCols, ValidationError.DeleteTransExist, table);
						OnThrowValidation(e);
						var error = e.Message;
						if (String.IsNullOrEmpty(error))
							error = DbContext.Instance.Context.StringResource.DeleteTransExist(arrCols, e.ColumnNames, e.ColumnValues,
																																										e.TableName);
						throw new ValidateDeleteException(error, arrCols);
					}
				}

			}*/
			return true;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <exception cref="ValidattionException"></exception>
		/// <returns></returns>
		public virtual bool ValidateUpdate() {
			Validate();

			if (!RowId.ValueNotDefault || !Sfuvc.ValueNotDefault) {
				DbColumn col;
				if (RowId.ValueNotDefault)
					col = Sfuvc;
				else col = RowId;
				throw new ValidateUpdateException("For update statement, Must SELECT both RowId And Sfuvc columns in select statement", col);
			}
			ValidateUnique(false);
			//ValidateExist(false);



			return true;
		}
		private void ValidateUnique(bool isInsert) {
			for (var i = 0; i < Columns.Count; i++) {
				var column = Columns[i];
				if (column.Unique) {
					if (!isInsert && !column.ValueDbChange) continue;
					var exist = GlobalEntityMetadata.CreateInstance();
					var me = new OneEntityCreator(exist);
					var col = exist.Columns[i];

					QueryRun
						.Select(exist.GlobalEntityMetadata.RowId)
						.From(exist.GlobalEntityMetadata)
						.Where(new QueryExpBuilder(col.ColumnView, QueryOperator.Equal, column.DbValue).And(exist.RowId.ColumnView, QueryOperator.Diff, RowId.Value))
						.Excute(me, 1);

					if (me.Success) {
						var e = new ThrowValidationExceptionEventArgs(column, ValidationError.ValueExistInDb);
						OnThrowValidation(e);
						var error = e.Message;
						if (String.IsNullOrEmpty(error))
							error = DbContext.Instance.Context.StringResource.ColumnValueExist(column, e.ColumnName, e.ColumnValue, e.TableName);
						if (isInsert)
							throw new ValidateInsertException(error, column);
						throw new ValidateUpdateException(error, column);
					}
				}
			}
		}

		//private void ValidateExist(bool isInsert) {
		//  var parents = GlobalEntityMetadata.Parents();
		//  if (parents != null) {
		//    foreach (var relationView in parents) {
		//      DbColumnView[] columnViews;
		//      object[] values;
		//      GlobalEntityView table;
		//      if (ParseRelation(relationView.Details, "", out columnViews, out values, out table)) {
		//        QueryExpBuilder where = null;
		//        for (int i = 0; i < columnViews.Length; i++) {
		//          where = AndOrCreate(where, columnViews[i], QueryOperator.Equal, values[i]);
		//        }
		//        if (where == null) continue;// next relation

		//        if (table != null) {
		//          var parentCreator = new OneEntityCreator(table.CreateInstance());
		//          QueryRun
		//            .Select(table.Columns)
		//            .From(table)
		//            .Where(where)
		//            .Excute(parentCreator, 1);
		//          if (!parentCreator.Success) {
		//            var arrCols = relationView.Details.Where(view => view.Column != null).Select(view => Columns.FirstOrDefault(c => c.ColumnView == view.Column)).Where(col => col != null).ToArray();
		//            var e = new ThrowValidationExceptionEventArgs(arrCols, ValidationError.ValueNotExistInDb);
		//            OnThrowValidation(e);
		//            var error = e.Message;
		//            if (String.IsNullOrEmpty(error))
		//              error = DbContext.Instance.Context.StringResource.ColumnValueNotExist(arrCols, e.ColumnNames, e.ColumnValues, e.TableName);
		//            if (isInsert)
		//              throw new ValidateInsertException(error, arrCols);
		//            throw new ValidateUpdateException(error, arrCols);
		//          }
		//        }
		//      }

		//    }
		//  }
		//}

		//private bool ParseRelation(RelationDetailView[] relationViewsDetail, string method, out DbColumnView[] relationColumns, out object[] relationValues, out GlobalEntityView relationTable) {
		//  var cols = new List<DbColumnView>();
		//  var vs = new List<Object>();
		//  relationColumns = null;
		//  relationValues = null;
		//  relationTable = null;

		//  if (relationViewsDetail == null) return false;
		//  if (relationViewsDetail.Length == 0) return false;
		//  foreach (var relationView in relationViewsDetail) {
		//    if (relationView.RefColumn == null) {
		//      throw new EnterpriseFramewordException(String.Format("Relation from table [{0}] has RefColumn null. Recheck {1} method from table [{0}] metadata", TableName, method));
		//    }
		//    if (relationTable == null) {
		//      relationTable = relationView.RefColumn.TableView as GlobalEntityView;
		//      if (relationTable == null)
		//        throw new EnterpriseFramewordException(String.Format("Relation from table [{0}] refer to a table which metadata is not inherited from GlobalEntityView. Recheck {1} method from table [{0}] metadata", TableName, method));
		//    }
		//    RelationDetailView viewDetail = relationView;
		//    if (relationView.Column != null) {

		//      var column = Columns.FirstOrDefault(c => c.ColumnView == viewDetail.Column);
		//      if (column == null) {
		//        throw new EnterpriseFramewordException(String.Format("Relation from table [{0}] has column [{1}] does not ReferenceEquals any columns in table [{0}]. Recheck Parents {2} from table [{0}] metadata", TableName, viewDetail.Column.ColumnName, method));
		//      }
		//      if (column.ValueDbChange) {
		//        cols.Add(viewDetail.RefColumn);
		//        vs.Add(column.DbValue);
		//      }
		//    } else {
		//      cols.Add(viewDetail.RefColumn);
		//      vs.Add(viewDetail.Value);
		//    }
		//    if (relationTable != relationView.RefColumn.TableView) {
		//      throw new EnterpriseFramewordException(String.Format("Relation from table [{0}] refer to many table. Recheck {1} method from table [{0}] metadata", TableName, method));
		//    }

		//  }
		//  relationColumns = cols.ToArray();
		//  relationValues = vs.ToArray();
		//  if (relationColumns.Length == 0) return false;
		//  return true;
		//}


		/// <summary>
		/// Load entity from database by RowId
		/// </summary>
		/// <returns>true if load succuessfully, otherwise return false</returns>
		public virtual bool LoadByRowId() {
			return LoadBy(RowId);
		}



		//protected virtual void LoadRelation(EntityCreator creator, RelationDetailView[] relationViewsDetail) {
		//  if (relationViewsDetail == null || relationViewsDetail.Length == 0) {
		//    throw new ArgumentNullException("relationViewsDetail");
		//  }
		//  QueryExpBuilder where = null;
		//  GlobalEntityView childMetadata = null;

		//  foreach (var relationView in relationViewsDetail) {
		//    if (childMetadata == null)
		//      childMetadata = (GlobalEntityView)relationView.Column.TableView;
		//    else {
		//      var localChild = (GlobalEntityView)relationView.Column.TableView;
		//      if (localChild.TableName != childMetadata.TableName) {
		//        throw new EnterpriseEntityException("Columns in relationview is not belong to unique table");
		//      }
		//    }

		//    if (relationView.RefColumn == null) {
		//      where = AndOrCreate(where, relationView.Column, QueryOperator.Equal, relationView.Value);
		//    } else {
		//      var column = Columns.Find(relationView.Column.ColumnIndex);
		//      where = AndOrCreate(where, relationView.Column, QueryOperator.Equal, column.DbValue);
		//    }
		//  }
		//  if (childMetadata == null) {
		//    throw new EnterpriseEntityException("Child table is not found in relation");
		//  }
		//  QueryRun
		//    .Select(childMetadata.Columns)
		//    .From(childMetadata)
		//    .Where(where)
		//    .Excute(creator);
		//}

		/// <summary>
		/// Load entity by specific columns
		/// </summary>
		/// <param name="columns"></param>
		/// <returns></returns>
		public virtual bool LoadBy(params DbColumn[] columns) {
			if (columns.Length == 0) return false;

			var exp = BuildWhereInLoadMethod(columns);
			if (exp == null) return false;
			var me = new OneEntityCreator(this);

			QueryRun.Select(GlobalEntityMetadata.Columns).From(GlobalEntityMetadata).Where(exp).Excute(me, 1);
			if (me.Success) MarkDelete = false;
			return me.Success;
		}

		internal virtual QueryExpBuilder BuildWhereInLoadMethod(params DbColumn[] columns) {
			var run = columns[0];
			var exp = new QueryExpBuilder(run.ColumnView, QueryOperator.Equal, run.DbValue);
			for (int i = 1; i < columns.Length; i++) {
				run = columns[i];
				exp = exp.And(run.ColumnView, QueryOperator.Equal, run.DbValue);
			}
			return exp;
		}

		/// <summary>
		/// Get transaction at the top pool
		/// </summary>
		/// <returns></returns>
		protected ITransaction GetCurrentTran() {
			return TransactionManager.GetCurrentTran();
		}
		///<summary>
		///</summary>
		//public interface IEntityCreator {
		//  /// <summary>
		//  /// 
		//  /// </summary>
		//  /// <param name="entity"></param>
		//  void Commit(GlobalEntity entity);
		//  /// <summary>
		//  /// 
		//  /// </summary>
		//  /// <returns></returns>
		//  GlobalEntity[] CreateEntities();
		//  /// <summary>
		//  /// Inform create collection completed. for internal use only
		//  /// </summary>
		//  void CollectionComplete();
		//}
		/// <summary>
		/// Base class for creating collection from result of excuting select statement
		/// </summary>
		public class EntityCreator {
			/// <summary>
			/// List of entity is result of selection statement
			/// </summary>
			/// <returns></returns>
			public virtual GlobalEntity[] CreateEntities() {
				return new GlobalEntity[0];
			}

			//protected virtual GlobalEntity CreateEntity() {
			//  return null;
			//}
			/// <summary>
			/// Inform entity is fully fill from database
			/// </summary>
			/// <param name="entity"></param>
			public virtual void Commit(GlobalEntity entity) {

			}
			internal virtual void CollectionComplete() {

			}
		}



		private class GlobalViewCreator : EntityCreator {
			public readonly List<GlobalEntity> Entities;
			private readonly GlobalEntityView _view;
			public GlobalViewCreator(GlobalEntityView view) {
				_view = view;
				Entities = new List<GlobalEntity>();
			}
			public override GlobalEntity[] CreateEntities() {
				return new[] { _view.CreateInstance() };
			}
			//protected override GlobalEntity CreateEntity() {
			//  return _view.CreateInstance();
			//}
			public override void Commit(GlobalEntity entity) {
				Entities.Add(entity);
			}
		}

		///<summary>
		///</summary>
		public class OneEntityCreator : EntityCreator {
			///<summary>
			///</summary>
			public bool Success { get; protected set; }
			protected readonly GlobalEntity Entity;
			///<summary>
			///</summary>
			///<param name="entity"></param>
			public OneEntityCreator(GlobalEntity entity) {
				Entity = entity;
			}
			public override GlobalEntity[] CreateEntities() {
				return new[] { Entity };
			}
			//protected override GlobalEntity CreateEntity() {
			//  return Entity;
			//}
			public override void Commit(GlobalEntity entity) {
				base.Commit(entity);
				Success = true;
			}
		}

		/// <summary>
		/// Generic create collection of entities
		/// </summary>
		/// <typeparam name="T"></typeparam>
		public class CollectionCreator<T> : EntityCreator where T : GlobalEntity, new() {
			protected readonly ICollection<T> Collection;
			/// <summary>
			/// Contrcutor
			/// </summary>
			/// <param name="collection"></param>
			public CollectionCreator(ICollection<T> collection) {
				Collection = collection;
			}
			public override GlobalEntity[] CreateEntities() {
				return new GlobalEntity[] { new T() };
			}
			//protected override GlobalEntity CreateEntity() {
			//  return new T();
			//}
			public override void Commit(GlobalEntity entity) {
				Collection.Add((T)entity);
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="TP"></typeparam>
		/// <typeparam name="TC"></typeparam>
		public class One2ManyRelationCreator<TP, TC> : CollectionCreator<TP>
			where TP : GlobalEntity, new()
			where TC : GlobalEntity, new() {
			private readonly AndRelationCollection _relations;
			private readonly GlobalEntityView _childMetadata;
			/// <summary>
			/// 
			/// </summary>
			/// <param name="collection"></param>
			/// <param name="childMetadata"></param>
			/// <param name="relations"></param>
			public One2ManyRelationCreator(ICollection<TP> collection, GlobalEntityView childMetadata, AndRelationCollection relations)

				: base(collection) {
				_relations = relations;
				_childMetadata = childMetadata;
			}

			internal override void CollectionComplete() {
				if (_relations != null && _relations.ColumnRelations.Count > 0 && _childMetadata != null) {

					if (_relations.ColumnRelations.Count == 1) {
						BuildOne();
					} else {
						BuildMany();
					}
				}
			}
			private void BuildMany() {
				var items = new List<TP>();
				foreach (var p in Collection) {
					if (items.Count == 10) {
						BuildManyFrom(items);
						items.Clear();
					} else {
						items.Add(p);
					}
				}
				BuildManyFrom(items);
			}
			private void BuildManyFrom(List<TP> parents) {
				if (parents.Count == 0) return;
				QueryExpBuilder allColumn = null;
				foreach (var parent in parents) {
					QueryExpBuilder columnWhere = null;
					foreach (var relation in _relations.ColumnRelations) {
						columnWhere = AndOrCreate(columnWhere, _childMetadata.Columns[relation.ChildColumnIndex], QueryOperator.Equal,
																			parent.Columns[relation.ParentColumnIndex]);
					}

					if (allColumn == null) allColumn = columnWhere;
					else {
						allColumn = allColumn.Add(columnWhere, ExpressionOperator.Or);
					}
				}

				QueryExpBuilder qValue = null;
				foreach (var valueRelation in _relations.ValueRelations) {
					qValue = AndOrCreate(qValue, _childMetadata.Columns[valueRelation.ChildColumnIndex], QueryOperator.Equal,
															 valueRelation.Value);
				}

				QueryExpBuilder where;
				if (allColumn != null && qValue != null) {
					where = allColumn.Add(qValue, ExpressionOperator.And);
				} else if (allColumn != null) where = allColumn;
				else where = qValue;
				QueryRun
					.Select(_childMetadata.Columns)
					.From(_childMetadata)
					.Where(where)
					.Excute(new ChildCreator(parents, _relations, this));
			}
			private void BuildOne() {
				var items = new List<TP>();
				foreach (var p in Collection) {
					if (items.Count == 10) {
						BuildOneFrom(items);
						items.Clear();
					} else {
						items.Add(p);
					}
				}
				BuildOneFrom(items);
			}
			private void BuildOneFrom(List<TP> parents) {

				if (parents.Count == 0) return;
				var relation = _relations.ColumnRelations[0];
				var values = new ArrayList();
				foreach (var parent in parents) {
					values.Add(parent.Columns[relation.ParentColumnIndex]);
				}
				var where = new QueryExpBuilder(_childMetadata.Columns[relation.ChildColumnIndex], QueryOperator.In, values);
				foreach (var valueRelation in _relations.ValueRelations) {
					where = where.And(_childMetadata.Columns[valueRelation.ChildColumnIndex], QueryOperator.Equal, valueRelation.Value);
				}
				QueryRun
					.Select(_childMetadata.Columns)
					.From(_childMetadata)
					.Where(where)
					.Excute(new ChildCreator(parents, _relations, this));
			}


			private class ChildCreator : EntityCreator {
				private readonly List<ParentKeyChildValue> _parentKv;
				private readonly AndRelationCollection _relations;
				private readonly One2ManyRelationCreator<TP, TC> _one2ManyRelationCreator;
				private TC _child;
				public ChildCreator(IEnumerable<TP> parents, AndRelationCollection relations, One2ManyRelationCreator<TP, TC> one2ManyRelationCreator) {
					_one2ManyRelationCreator = one2ManyRelationCreator;
					_relations = relations;
					_parentKv = new List<ParentKeyChildValue>();
					foreach (var parent in parents) {
						_parentKv.Add(new ParentKeyChildValue { Parent = parent });
					}
				}
				public override GlobalEntity[] CreateEntities() {
					_child = new TC();
					return new GlobalEntity[] { _child };
				}
				//protected override GlobalEntity CreateEntity() {
				//  _child = new TC();
				//  return _child;
				//}
				public override void Commit(GlobalEntity entity) {
					//base.Commit(entity);
					foreach (var childValue in _parentKv) {
						bool matchParent = true;

						foreach (var relation in _relations.ColumnRelations) {
							if (relation.ParentColumnIndex != -1) {
								var p = childValue.Parent.Columns[relation.ParentColumnIndex].DbValue;
								var c = entity.Columns[relation.ChildColumnIndex].DbValue;
								if (p == null) matchParent = c == null;
								else matchParent = p.Equals(c);

							}

							if (!matchParent) break;
						}
						if (matchParent) {
							if (childValue.Childs == null) {
								if (_one2ManyRelationCreator.ChildCreateNew != null) {
									var e = new ChildCreateNewEventArgs(childValue.Parent);
									_one2ManyRelationCreator.ChildCreateNew(_one2ManyRelationCreator, e);
									childValue.Childs = e.Childs;
								}
							}
							if (childValue.Childs != null) {
								childValue.Childs.Add(_child);
							}
							break;
						}
					}
				}
			}
			/// <summary>
			/// 
			/// </summary>
			public event EventHandler<ChildCreateNewEventArgs> ChildCreateNew;
			/// <summary>
			/// 
			/// </summary>
			public class ChildCreateNewEventArgs : EventArgs {
				/// <summary>
				/// 
				/// </summary>
				/// <param name="parent"></param>
				public ChildCreateNewEventArgs(TP parent) {
					Parent = parent;
				}
				/// <summary>
				/// 
				/// </summary>
				public TP Parent { get; private set; }
				/// <summary>
				/// 
				/// </summary>
				public ICollection<TC> Childs { get; set; }
			}

			private class ParentKeyChildValue {
				public TP Parent;
				public ICollection<TC> Childs;
			}



		}
		/// <summary>
		/// 
		/// </summary>
		protected struct AndRelation {
			/// <summary>
			/// 
			/// </summary>
			/// <param name="childColumn"></param>
			/// <param name="childValue"></param>
			public AndRelation(DbColumn childColumn, object childValue) {
				ChildColumnIndex = childColumn.ColumnIndex;
				ParentColumnIndex = -1;
				ChildValue = childValue;
			}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="childColumn"></param>
			/// <param name="parentColumn"></param>
			public AndRelation(DbColumn childColumn, DbColumn parentColumn) {
				ChildColumnIndex = childColumn.ColumnIndex;
				ParentColumnIndex = parentColumn.ColumnIndex;
				ChildValue = null;
			}
			/// <summary>
			/// 
			/// </summary>
			public readonly int ParentColumnIndex;// { get; private set; }
			/// <summary>
			/// 
			/// </summary>
			public readonly int ChildColumnIndex;// { get; private set; }
			/// <summary>
			/// 
			/// </summary>
			public readonly object ChildValue;// { get; private set; }
		}
		/// <summary>
		/// 
		/// </summary>
		public struct ColumnRelation {
			/// <summary>
			/// 
			/// </summary>
			/// <param name="childColumnIndex"></param>
			/// <param name="parentColumnIndex"></param>
			public ColumnRelation(int childColumnIndex, int parentColumnIndex) {
				ChildColumnIndex = childColumnIndex;
				ParentColumnIndex = parentColumnIndex;
			}
			/// <summary>
			/// 
			/// </summary>
			public readonly int ChildColumnIndex;
			/// <summary>
			/// 
			/// </summary>
			public readonly int ParentColumnIndex;
		}
		/// <summary>
		/// 
		/// </summary>
		public struct ValueRelation {
			/// <summary>
			/// 
			/// </summary>
			/// <param name="childColumnIndex"></param>
			/// <param name="value"></param>
			public ValueRelation(int childColumnIndex, object value) {
				ChildColumnIndex = childColumnIndex;
				Value = value;
			}
			/// <summary>
			/// 
			/// </summary>
			public readonly int ChildColumnIndex;
			/// <summary>
			/// 
			/// </summary>
			public readonly object Value;
		}
		/// <summary>
		/// 
		/// </summary>
		public class AndRelationCollection {
			/// <summary>
			/// 
			/// </summary>
			public AndRelationCollection() {
				ColumnRelations = new List<ColumnRelation>();
				ValueRelations = new List<ValueRelation>();
			}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="child"></param>
			/// <param name="parent"></param>
			public AndRelationCollection(DbColumnView child, DbColumnView parent)
				: this() {
				AndEqual(child, parent);
			}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="child"></param>
			/// <param name="value"></param>
			public AndRelationCollection(DbColumnView child, object value)
				: this() {
				AndEqual(child, value);
			}
			/// <summary>
			/// 
			/// </summary>
			public readonly List<ColumnRelation> ColumnRelations;
			/// <summary>
			/// 
			/// </summary>
			public readonly List<ValueRelation> ValueRelations;
			/// <summary>
			/// 
			/// </summary>
			/// <param name="child"></param>
			/// <param name="parent"></param>
			/// <returns></returns>
			public AndRelationCollection AndEqual(DbColumnView child, DbColumnView parent) {
				if (child == null) throw new ArgumentNullException("child");
				if (parent == null) throw new ArgumentNullException("parent");
				ColumnRelations.Add(new ColumnRelation(child.ColumnIndex, parent.ColumnIndex));
				return this;
			}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="child"></param>
			/// <param name="value"></param>
			/// <returns></returns>
			public AndRelationCollection AndEqual(DbColumnView child, object value) {
				if (child == null) throw new ArgumentNullException("child");
				if (value == null) throw new ArgumentNullException("value");
				ValueRelations.Add(new ValueRelation(child.ColumnIndex, value));
				return this;
			}
		}

		private EntityState _oldState;
		private bool _oldMarkDeleted;
		/// <summary>
		/// backup all columns value such as: backup original value, DbValue and state
		/// </summary>
		public virtual void BeginEdit() {
			foreach (var column in Columns) {
				column.BackupValue();
			}
			_oldMarkDeleted = IsMarkDelete;
			_oldState = State;
		}
		/// <summary>
		/// Clear backup value from BeginEdit method
		/// </summary>
		public virtual void EndEdit() {
			foreach (var column in Columns) {
				column.ClearBackup();
			}
		}
		/// <summary>
		/// Restore value from backup value
		/// </summary>
		public virtual void CancelEdit() {
			foreach (var column in Columns) {
				column.RestoreValue();
				column.ClearBackup();
			}

			State = _oldState;
			IsMarkDelete = _oldMarkDeleted;
		}
		/// <summary>
		/// Count by specific column with value
		/// </summary>
		/// <param name="columns"></param>
		/// <returns></returns>
		public long CountBy(params  DbColumn[] columns) {
			var oldId = RowId.Value;
			QueryExpBuilder where = columns.Aggregate<DbColumn, QueryExpBuilder>(null, (current, dbColumn) => AndOrCreate(current, dbColumn.ColumnView, QueryOperator.Equal, dbColumn.DbValue));
			var me = new OneEntityCreator(this);
			QueryRun
				.Select(RowId.ColumnView)
				.From(GlobalEntityMetadata)
				.Count(RowId.ColumnView)
				.Where(where)
				.Excute(me, 1);
			var value = RowId.Value;
			RowId.Value = oldId;
			return value;
		}


	}
}
